/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.netease.financial.base.photo;

import android.app.Activity;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.provider.MediaStore;
import android.view.View;
import android.widget.Toast;

import com.netease.financial.R;
import com.netease.financial.common.util.NfsLog;
import com.netease.financial.module.web.WebViewTabFragment;

import org.fireking.app.imagelib.entity.ImageBean;
import org.fireking.app.imagelib.widget.PicSelectActivity;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;

/**
 * Handles displaying a photo selection popup for a given photo view and dealing with the results
 * that come back.
 */
public abstract class PhotoSelectionHandler implements WebViewTabFragment.ActivityResultHandler, View.OnClickListener {

    private static final String TAG = PhotoSelectionHandler.class.getSimpleName();

    private static final int REQUEST_CODE_CAMERA_WITH_DATA = 1001;
    private static final int REQUEST_CODE_PHOTO_PICKED_WITH_DATA = 1002;
    private static final int REQUEST_CROP_PHOTO = 1003;
    private static final int REQUEST_CODE_MULTI_PHOTO_PICKED_WITH_DATA = 1004;

    // Default photo dimension to use if unable to query the provider.
    private static final int mDefaultPhotoDim = 720;

    protected final Context mContext;
    private final int mPhotoPickSize;
    private final Uri mCroppedPhotoUri;
    private final Uri mTempPhotoUri;

    public PhotoSelectionHandler(Context context) {
        mContext = context;
        mTempPhotoUri = PhotoUtils.generateTempImageUri(context);
        mCroppedPhotoUri = PhotoUtils.generateTempCroppedImageUri(mContext);
        mPhotoPickSize = getPhotoPickSize();
    }

    public abstract PhotoActionListener getListener();

    @Override
    public void onClick(View v) {
        final PhotoActionListener listener = getListener();

        if (listener != null) {
            Dialog photoActionDialog = PhotoActionPopup.createDialog(mContext, listener);
            photoActionDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
                @Override
                public void onDismiss(DialogInterface dialog) {
                    listener.onPhotoSelectionDismissed();
                }
            });

            photoActionDialog.show();
        }
    }

    /**
     * Attempts to handle the given activity result.  Returns whether this handler was able to
     * process the result successfully.
     *
     * @param requestCode The request code.
     * @param resultCode  The result code.
     * @param data        The intent that was returned.
     * @return Whether the handler was able to process the result.
     */
    @Override
    public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
        final PhotoActionListener listener = getListener();
        if (resultCode == Activity.RESULT_OK) {
            switch (requestCode) {
                // Cropped photo was returned
                case REQUEST_CROP_PHOTO: {
                    final Uri uri;
                    if (data != null && data.getData() != null) {
                        uri = data.getData();
                    } else {
                        uri = mCroppedPhotoUri;
                    }

                    try {
                        // delete the original temporary photo if it exists
                        mContext.getContentResolver().delete(mTempPhotoUri, null, null);
                        listener.onPhotoSelected(uri);
                        return true;
                    } catch (FileNotFoundException e) {
                        return false;
                    }
                }

                // Photo was successfully taken or selected from gallery, now crop it.
                case REQUEST_CODE_PHOTO_PICKED_WITH_DATA:
                case REQUEST_CODE_CAMERA_WITH_DATA:
                    final Uri uri;
                    boolean isWritable = false;
                    if (data != null && data.getData() != null) {
                        uri = data.getData();
                    } else {
                        uri = listener.getCurrentPhotoUri();
                        isWritable = true;
                    }

                    if (!listener.isPhotoNeedCrop()) {
                        try {
                            listener.onPhotoSelected(uri);
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                            return false;
                        }
                        return true;
                    }

                    final Uri toCrop;
                    if (isWritable) {
                        // Since this uri belongs to our file provider, we know that it is writable
                        // by us. This means that we don't have to save it into another temporary
                        // location just to be able to crop it.
                        toCrop = uri;
                    } else {
                        toCrop = mTempPhotoUri;
                        try {
                            PhotoUtils.savePhotoFromUriToUri(mContext, uri,
                                    toCrop, false);
                        } catch (SecurityException e) {
                            NfsLog.d(TAG, "Did not have read-access to uri : " + uri);
                            return false;
                        }
                    }

                    doCropPhoto(toCrop, mCroppedPhotoUri);
                    return true;

                case REQUEST_CODE_MULTI_PHOTO_PICKED_WITH_DATA: {
                    if (data != null && data.getExtras() != null) {
                        @SuppressWarnings("unchecked")
                        List<ImageBean> images = (List<ImageBean>) data.getSerializableExtra(PicSelectActivity.IMAGES);
                        List<Uri> uriList = new ArrayList<>();
                        for (ImageBean bean : images) {
                            uriList.add(Uri.fromFile(new File(bean.path)));
                        }
                        try {
                            listener.onMultiPhotoSelected(uriList);
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                            return false;
                        }
                        return true;

                    }
                }
            }
        }
        return false;
    }

    /**
     * Used by subclasses to delegate to their enclosing Activity or Fragment.
     */
    protected abstract void startPhotoActivity(Intent intent, int requestCode, Uri photoUri);

    /**
     * Sends a newly acquired photo to Gallery for cropping
     */
    private void doCropPhoto(Uri inputUri, Uri outputUri) {
        try {
            // Launch gallery to crop the photo
            final Intent intent = getCropImageIntent(inputUri, outputUri);
            startPhotoActivity(intent, REQUEST_CROP_PHOTO, inputUri);
        } catch (Exception e) {
            NfsLog.e(TAG, "Cannot crop image", e);
            Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
        }
    }

    /**
     * Should initiate an activity to take a photo using the camera.
     *
     * @param photoUri The file path that will be used to store the photo.  This is generally
     *                 what should be returned by
     *                 {@link PhotoActionListener#getCurrentPhotoUri()} ()}.
     */
    private void startTakePhotoActivity(Uri photoUri) {
        final Intent intent = getTakePhotoIntent(photoUri);
        startPhotoActivity(intent, REQUEST_CODE_CAMERA_WITH_DATA, photoUri);
    }

    /**
     * Should initiate an activity pick a photo from the gallery.
     *
     * @param photoUri The temporary file that the cropped image is written to before being
     *                 stored by the content-provider.
     *                 {@link PhotoSelectionHandler#handleActivityResult(int, int, Intent)}.
     */
    private void startPickFromGalleryActivity(Uri photoUri) {
        final Intent intent = getPhotoPickIntent(photoUri);
        startPhotoActivity(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA, photoUri);
    }

    private void startPickMultiPictureActivity() {
        final Intent intent = getMultiPhotoPickIntent();
        startPhotoActivity(intent, REQUEST_CODE_MULTI_PHOTO_PICKED_WITH_DATA, null);

    }

    private int getPhotoPickSize() {
        return mDefaultPhotoDim;
    }

    /**
     * Constructs an intent for capturing a photo and storing it in a temporary output uri.
     */
    private Intent getTakePhotoIntent(Uri outputUri) {
        final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE, null);
        PhotoUtils.addPhotoPickerExtras(intent, outputUri);
        return intent;
    }

    /**
     * Constructs an intent for picking a photo from Gallery, and returning the bitmap.
     */
    private Intent getPhotoPickIntent(Uri outputUri) {
        final Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
        intent.setType("image/*");
        PhotoUtils.addPhotoPickerExtras(intent, outputUri);
        return intent;
    }

    private Intent getMultiPhotoPickIntent() {
        final Intent intent = new Intent(mContext, PicSelectActivity.class);
        intent.putExtra(PicSelectActivity.ARG_MAX_COUNT, getListener().getMaxPhotoCount());
        return intent;
    }

    /**
     * Constructs an intent for image cropping.
     */
    private Intent getCropImageIntent(Uri inputUri, Uri outputUri) {
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(inputUri, "image/*");
        PhotoUtils.addPhotoPickerExtras(intent, outputUri);
        PhotoUtils.addCropExtras(intent, mPhotoPickSize);
        return intent;
    }

    public abstract class PhotoActionListener implements PhotoActionPopup.Listener {

        @Override
        public void onTakePhotoChosen() {
            try {
                // Launch camera to take photo
                startTakePhotoActivity(mTempPhotoUri);
            } catch (ActivityNotFoundException e) {
                Toast.makeText(
                        mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
            }
        }

        @Override
        public void onPickFromGalleryChosen() {
            try {
                // Launch picker to choose photo
                if (getListener().isPhotoNeedCrop()) {
                    startPickFromGalleryActivity(mTempPhotoUri);
                } else {
                    startPickMultiPictureActivity();
                }
            } catch (ActivityNotFoundException e) {
                Toast.makeText(
                        mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
            }
        }

        /**
         * Called when the user has completed selection of a photo.
         *
         * @throws FileNotFoundException
         */
        public abstract void onPhotoSelected(Uri uri) throws FileNotFoundException;

        public abstract void onMultiPhotoSelected(List<Uri> uriList) throws FileNotFoundException;

        public abstract boolean isPhotoNeedCrop();

        public abstract int getMaxPhotoCount();

        /**
         * Gets the current photo file that is being interacted with.  It is the activity or
         * fragment's responsibility to maintain this in saved state, since this handler instance
         * will not survive rotation.
         */
        public abstract Uri getCurrentPhotoUri();

        /**
         * Called when the photo selection dialog is dismissed.
         */
        public abstract void onPhotoSelectionDismissed();
    }
}
